
/**
  ******************************************************************************
  * Copyright 2021 The Microbee Authors. All Rights Reserved.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  * 
  * http://www.apache.org/licenses/LICENSE-2.0
  * 
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  * 
  * @file       storage_manager.c
  * @author     baiyang
  * @date       2021-8-8
  ******************************************************************************
  */

/*----------------------------------include-----------------------------------*/
#include "storage_manager.h"

#include <stddef.h>

#include <common/console/console.h>
/*-----------------------------------macro------------------------------------*/
#ifndef MIN
#define MIN(a, b)         ((a) < (b) ? (a) : (b))
#endif
/*----------------------------------typedef-----------------------------------*/

/*---------------------------------prototype----------------------------------*/

/*----------------------------------variable----------------------------------*/
#if !defined(RT_USING_FAL)
static uint8_t buff_mission[STORAGE_MISSION_LENGTH];
static uint8_t buff_keys[STORAGE_KEYS_LENGTH];
#endif
/*-------------------------------------os-------------------------------------*/

/*----------------------------------function----------------------------------*/
void StorageAccess_init(storage_access_t storage, StorageType type)
{
    storage->type = type;
    storage->total_size = 0;

#if defined(RT_USING_FAL)
    if (storage->type == StorageMission) {
        storage->storage_dev = fal_partition_find("mission");
    } else if (storage->type == StorageKeys){
        storage->storage_dev = fal_partition_find("signing_key");
    } else {
        storage->storage_dev = NULL;
    }

    if (storage->storage_dev != NULL) {
        storage->total_size = storage->storage_dev->len;
    }
#else
    storage->buff = NULL;

    if (storage->type == StorageMission) {
        storage->total_size = sizeof(buff_mission);
        storage->buff = &buff_mission[0];
    } else if (storage->type == StorageKeys){
        storage->total_size = sizeof(buff_keys);
        storage->buff = &buff_keys[0];
    }
#endif
}

/*
  base read function. The src offset is within the bytes allocated
  for the storage type of this StorageAccess object
*/
bool StorageAccess_read_block(const struct StorageAccess* storage, void *data, uint32_t addr, uint32_t n)
{
#if defined(RT_USING_FAL)
    if (storage->storage_dev == NULL) {
        return false;
    }

    int len = fal_partition_read(storage->storage_dev, addr, (uint8_t *)data, (size_t)n);

    return (len == n);
#else
    if (storage->buff == NULL) {
        return false;
    }

    if(n <= 0)
    {
        return false;
    }

    uint8_t *b = (uint8_t *)data;
    for (uint32_t i=0; i<storage->total_size; i++) {
        if (addr+i >= storage->total_size)
        {
            return false;
        }
        
        b[i] = storage->buff[addr+i];
        n--;
        if (n == 0) {
            break;
        }
    }

    return (n == 0);
#endif
}


/*
  base read function. The addr offset is within the bytes allocated
  for the storage type of this StorageAccess object
*/
bool StorageAccess_write_block(const struct StorageAccess* storage, uint32_t addr, const void *data, uint32_t n)
{
#if defined(RT_USING_FAL)
    if (storage->storage_dev == NULL) {
        return false;
    }

    int len = fal_partition_write(storage->storage_dev, addr, (const uint8_t *)data, (size_t)n);

    return (len == n);
#else
    if (storage->buff == NULL) {
        return false;
    }

    if(n <= 0)
    {
        return false;
    }
    
    const uint8_t *b = (const uint8_t *)data;
    for (uint32_t i=0; i<storage->total_size; i++) {
        if (addr+i >= storage->total_size)
        {
            return false;
        }
        
        storage->buff[addr+i] = b[i];
        n--;
        if (n == 0) {
            break;
        }
    }
    return (n == 0);
#endif
}

/*
  copy one area to another
 */
bool StorageAccess_copy_area(storage_access_t storage, const struct StorageAccess* source)
{
    // we deliberately allow for copies from smaller areas. This
    // allows for a partial backup region for parameters
    uint32_t total = MIN(StorageAccess_size(source), StorageAccess_size(storage));
    uint32_t ofs = 0;
    while (total > 0) {
        uint8_t block[32];
        uint32_t n = MIN(sizeof(block), total);
        if (!StorageAccess_read_block(source, block, ofs, n) ||
            !StorageAccess_write_block(storage, ofs, block, n)) {
            return false;
        }
        total -= n;
        ofs += n;
    }
    return true;
}

/*
  erase all storage
 */
void StorageAccess_erase(storage_access_t storage)
{
#if defined(RT_USING_FAL)
    if ((storage->storage_dev == NULL) || !(fal_partition_erase_all(storage->storage_dev) >= 0)) {
        console_printf("StorageManager: erase failed\n");
    }
#endif
}

/*------------------------------------test------------------------------------*/


